package de.popforge.audio 
{
	import __AS3__.vec.Vector;
	
	import de.popforge.audio.driver.AudioDriver;
	import de.popforge.audio.driver.IAudioProcessor;	

	/**
	 * Simple flanger with a lfo controlled read offset and a static width
	 * 
	 * @author Andre Michelle
	 */
	public class SimpleFlanger
		implements IAudioProcessor
		 
	{
		private const delayLine: Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>( 2, true );
		
		private var _wetLevel: Number;
		private var _dryLevel: Number;
		private var _feedback: Number;

		private var _delaySize: int;
		private var _delayHalf: int;
		private var _lfoPhase: Number;
		private var _lfoDepth: Number;
		private var _lfoAdd: Number;

		private var _writeIndex: int;
		
		public function SimpleFlanger( delayMs: Number, lfoSpeedMs: Number, lfoDepth: Number, wetLevel: Number, dryLevel: Number, feedback: Number )
		{
			_delayHalf = int( delayMs * 44.1 ); // delayMs @44.1KHz in samples
			_delaySize = ( _delayHalf << 1 ) + 1; // create more space to move read pointer
		
			_lfoAdd = 1.0 / ( lfoSpeedMs * 44.1 ); // 1 cycle/lfoSpeedMs
			_lfoDepth = lfoDepth;
			_dryLevel = dryLevel;
			_wetLevel = wetLevel;
			_feedback = feedback;
			
			_writeIndex = 0;
			_lfoPhase = 0.0;
			
			delayLine[0] = new Vector.<Number>( _delaySize, true );
			delayLine[1] = new Vector.<Number>( _delaySize, true );
		}

		public function process( buffer: Vector.<Vector.<Number>> ): void
		{
			var leDelayLine: Vector.<Number> = delayLine[0];
			var riDelayLine: Vector.<Number> = delayLine[1];

			var leBuffer: Vector.<Number> = buffer[0];
			var riBuffer: Vector.<Number> = buffer[1];
			
			var lfoValue: Number;
			
			var leReadAmp: Number;
			var riReadAmp: Number;
			
			var leReadIndex: Number;
			var riReadIndex: Number;
			
			var i: int = 0;
			var n: int = AudioDriver.SIZE;
			
			for( ; i < n ; ++i )
			{
				//-- compute LFO (triangle)
				lfoValue = _lfoPhase < .5 ? _lfoPhase * 2.0 : 2.0 - _lfoPhase * 2.0;
				
				//-- move LFO in time
				_lfoPhase += _lfoAdd;
				if( _lfoPhase >= 1.0 ) --_lfoPhase;
				
				lfoValue *= _lfoDepth;
				
				//-- LEFT
				//-- read from the half with positive offset
				leReadIndex = _writeIndex - ( _delayHalf + _delayHalf * lfoValue );
				
				//-- clamp
				if( leReadIndex < 0 )
					leReadIndex += _delaySize;
				
				//-- RIGHT
				//-- read from the half with negative offset
				riReadIndex = _writeIndex - ( _delayHalf - _delayHalf * lfoValue );
				
				//-- clamp
				if( riReadIndex < 0 )
					riReadIndex += _delaySize;
					
				//-- write input to delay line (replace)
				leDelayLine[ _writeIndex ] = leBuffer[i];
				riDelayLine[ _writeIndex ] = riBuffer[i];
				
				//-- no interpolation (don't do this - creates artifacts)
				if( false )
				{
					leReadAmp = leDelayLine[ int( leReadIndex ) ];
					riReadAmp = riDelayLine[ int( riReadIndex ) ];
				}
				else
				{
					leReadAmp = readLinearInterpolated( leDelayLine, leReadIndex );
					riReadAmp = readLinearInterpolated( riDelayLine, riReadIndex );
				}
				
				//-- write read feedback to delay line (add)
				leDelayLine[ _writeIndex ] += leReadAmp * _feedback;
				leDelayLine[ _writeIndex ] += riReadAmp * _feedback;

				//-- write read amplitudes into buffer (add > dry level = 1.0)
				leBuffer[i] = leBuffer[i] * _dryLevel + leReadAmp * _wetLevel;
				riBuffer[i] = riBuffer[i] * _dryLevel + riReadAmp * _wetLevel;
				
				//-- move write head
				if( ++_writeIndex == _delaySize )
					_writeIndex = 0;
			}
		}
		
		//-- if we only has inlines...
		private function readLinearInterpolated( line: Vector.<Number>, position: Number ): Number
		{
			var int0: int = int( position );
			var int1: int = ( int0 + 1 ) % line.length;
			
			var alpha: Number = position - int0;
			
			return line[ int0 ] * ( 1.0 - alpha ) + line[ int1 ] * alpha;
		}
	}
}
